package src

import (
	_ "embed"
	"encoding/json"
	"fmt"
	"net/http"
	"slices"
	"time"

	"gitee.com/haifengat/cas"
	ge "gitee.com/haifengat/gin-ex/v2"
	zd "gitee.com/haifengat/zorm-dm"
	"github.com/casbin/casbin/v2/model"
	"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/pkg/errors"
)

//go:embed model.ini
var model_rbac string

type policyRequest struct {
	Role   string `json:"role"`
	App    string `json:"app"`
	URL    string `json:"url"`
	Action string `json:"action"`
}

type enforceyRequest struct {
	UserName string `json:"userName"`
	App      string `json:"app"`
	URL      string `json:"url"`
	Action   string `json:"action"`
}

type userRoleRequest struct {
	Role  string   `json:"role"`
	Users []string `json:"users"`
}

type userRolesRequest struct {
	UserName string `json:"userName"`
}

type roleUsersRequest struct {
	Role string `json:"role"`
}

// RouteCas 设置与角色和权限管理相关的路由
// @param g Gin 的路由组
// @return 如果初始化 Casbin 服务失败，则返回错误
func RouteCas(g *gin.RouterGroup) error {
	// 初始化 Casbin 服务
	m, err := model.NewModelFromString(model_rbac)
	if err != nil {
		return errors.Wrap(err, "初始化 model")
	}
	a := &AdapterDM{}
	err = cas.InitializeCasbinService(m, a)
	if err != nil {
		return errors.Wrap(err, "初始化 Adapter")
	}

	// 处理 Casdoor 用户信息
	g.Any("/UserInfo", ge.CreateQryHandler(processCasdoorUser))

	// 获取应用
	g.GET("/Application", ge.CreateQryHandler(func(ctx *gin.Context) (data any, total int, errRtn error) {
		data, errRtn = casdoorsdk.GetApplications()
		return
	}))

	// 设置 Role 相关的路由
	setupRoleRoutes(g)

	// 设置 Policy 相关的路由
	setupPolicyRoutes(g)

	// 权限(表)管理
	cas.RouteTable(g, map[string]zd.IEntity{"Permission": &Permission{}}, nil, ctxDao)

	// 测试权限
	g.POST("/Enforce", ge.CreateQryHandler(func(ctx *gin.Context) (data any, total int, errRtn error) {
		var req enforceyRequest
		if err := ctx.BindJSON(&req); err != nil {
			errRtn = errors.Wrap(err, "绑定 JSON")
			return
		}
		data, errRtn = cas.GetCasbinService().Enforce(req.UserName, req.App, req.URL, req.Action)
		return
	}))

	// 使用中间件进行权限检查
	g.Use(permissionMiddleware)
	return nil
}

// setupRoleRoutes 设置与角色相关的路由
func setupRoleRoutes(g *gin.RouterGroup) {
	// 获取角色
	g.GET("/Role", ge.CreateQryHandler(func(ctx *gin.Context) (data any, total int, errRtn error) {
		// 获取角色
		roles, err := casdoorsdk.GetRoles()
		if err != nil {
			errRtn = errors.Wrap(err, "取角色")
			return
		}
		total = len(roles)

		// 获取策略(角色↔权限)
		policies, err := zd.Select[Policy](ctxDao, nil, nil)
		if err != nil {
			errRtn = errors.Wrap(err, "取权限")
			return
		}
		// 获取权限
		permissions, err := zd.Select[Permission](ctxDao, nil, nil)
		if err != nil {
			errRtn = errors.Wrap(err, "取权限")
			return
		}
		// 权限嵌入到角色
		type V_Role struct {
			casdoorsdk.Role
			Permissions []Permission
		}
		rtnRows := make([]V_Role, 0, len(roles))
		for _, role := range roles {
			rtnRow := V_Role{Role: *role}
			for _, policy := range policies {
				if policy.Role == role.Name {
					for _, permission := range permissions {
						if permission.UUID == policy.PermissionID {
							rtnRow.Permissions = append(rtnRow.Permissions, permission)
						}
					}
				}
			}
			rtnRows = append(rtnRows, rtnRow)
		}
		data = rtnRows
		return
	}))

	// 取角色名
	g.GET("/Role/Name", ge.CreateQryHandler(func(ctx *gin.Context) (data any, total int, errRtn error) {
		roles, err := casdoorsdk.GetRoles()
		if err != nil {
			errRtn = errors.Wrap(err, "取角色")
			return
		}
		ids := make([]string, 0)
		for _, role := range roles {
			ids = append(ids, role.Name)
		}
		data = ids
		return
	}))

	// 添加角色
	g.POST("/Role", ge.CreateUpHandler(func(ctx *gin.Context) (errRtn error) {
		var req struct {
			Name        string `json:"name"`
			DisplayName string `json:"displayName"`
		}
		if err := ctx.BindJSON(&req); err != nil {
			errRtn = errors.Wrap(err, "绑定 JSON")
			return
		}
		_, errRtn = casdoorsdk.AddRole(&casdoorsdk.Role{
			Owner:       orgName,
			Name:        req.Name,
			DisplayName: req.DisplayName,
			IsEnabled:   true,
		})
		return
	}))

	g.PUT("/Role", ge.CreateUpHandler(func(ctx *gin.Context) (errRtn error) {
		where, set, err := ge.PutParams(ctx)
		if err != nil {
			errRtn = errors.Wrap(err, "取Put参数")
			return
		}
		if len(where) == 0 || len(set) == 0 {
			errRtn = errors.New("where/set 为空")
			return
		}
		role, err := casdoorsdk.GetRole(where["name"].(string))
		if err != nil {
			errRtn = errors.Wrap(err, "查角色错误")
			return
		}
		if tmp, ok := set["users"]; ok {
			users, ok := tmp.([]any)
			if !ok {
				errRtn = errors.New("users 类型错误")
				return
			}
			// users 由 []any 转为 []string
			role.Users = make([]string, len(users))
			for i, v := range users {
				role.Users[i] = v.(string)
			}
		}
		_, errRtn = casdoorsdk.UpdateRole(role)
		return
	}))

	// 删除角色
	g.DELETE("/Role", ge.CreateUpHandler(func(ctx *gin.Context) (errRtn error) {
		var req struct {
			Name string `json:"name"`
		}
		if err := ctx.BindJSON(&req); err != nil {
			errRtn = errors.Wrap(err, "绑定 JSON")
			return
		}
		_, errRtn = casdoorsdk.DeleteRole(&casdoorsdk.Role{
			Owner: orgName,
			Name:  req.Name,
		})
		return
	}))

	// 添加用户角色
	g.POST("/addUserToRole", ge.CreateUpHandler(addUserToRoleHandler))
	// 移除用户角色
	g.POST("/removeUserFromRole", ge.CreateUpHandler(removeUserToRoleHandler))
	// 获取角色用户
	g.POST("/getRoleUsers", ge.CreateQryHandler(getRoleUsersHandler))
	// 获取用户角色
	g.POST("/getUserRoles", ge.CreateQryHandler(getUserRolesHandler))
}

// setupPolicyRoutes 设置与策略相关的路由
func setupPolicyRoutes(g *gin.RouterGroup) {
	// 获取策略
	g.POST("/get/Policy", ge.CreateQryHandler(func(ctx *gin.Context) (data any, total int, errRtn error) {
		where, current, pageSize, sqlAppend, err := ge.PostGetParams(ctx)
		if err != nil {
			errRtn = errors.Wrap(err, "获取参数")
			return
		}
		var p *zd.PageInfo
		if current > 0 {
			p = &zd.PageInfo{PageNo: current, PageSize: pageSize}
		}
		if len(where) == 0 {
			where = nil
		}
		rows, err := zd.SelectMapWithAppend[CasbinPolicy](ctxDao, p, where, sqlAppend)
		if err != nil {
			errRtn = errors.Wrap(err, "查询")
			return
		}
		data = rows
		if p != nil {
			total = p.TotalCount
		}
		return
	}))

	// 添加策略
	g.POST("/Policy", ge.CreateUpHandler(func(ctx *gin.Context) (errRtn error) {
		var reqPolicy struct {
			Role         string   `json:"role"`
			PermissionID []string `json:"permissionID"`
		}
		errRtn = ctx.BindJSON(&reqPolicy)
		if errRtn != nil {
			return
		}
		policies := make([]Policy, 0, len(reqPolicy.PermissionID))
		for _, permissionID := range reqPolicy.PermissionID {
			policies = append(policies, Policy{
				PType:        'p',
				Role:         reqPolicy.Role,
				PermissionID: UUIDType(permissionID),
				BaseColumn: BaseColumn{
					InsertTime: DateTimeType(time.Now().Local().Format(time.DateTime)),
				},
			})
		}
		_, errRtn = zd.Insert(ctxDao, true, policies...)
		return
	}))
	// 移除策略
	g.DELETE("/Policy", ge.CreateUpHandler(func(ctx *gin.Context) (errRtn error) {
		var req struct {
			Role         string `json:"role"`
			PermissionID string `json:"permissionID"`
		}

		// 绑定请求体并检查错误
		if err := ctx.BindJSON(&req); err != nil {
			errRtn = errors.Wrap(err, "绑定 JSON")
			return
		}
		n, err := zd.Delete[Policy](ctxDao, map[string]any{"PType": "p", "Role": req.Role, "PermissionID": req.PermissionID})
		if err != nil {
			errRtn = errors.Wrap(err, "删除")
			return
		}
		if n == 0 {
			errRtn = fmt.Errorf("未找到数据 %v", req)
		}
		return
	}))
}

// permissionMiddleware 是一个中间件生成函数，用于权限控制
//
// 它接受 CasbinService 实例作为参数，并返回一个 gin 的 HandlerFunc
// 该中间件用于在请求处理前验证用户权限
func permissionMiddleware(ctx *gin.Context) {
	// 获取用户信息（假设用户信息存储在 Claims 中）
	claims, err := cas.GetClaims(ctx)
	if err != nil {
		// 如果获取用户信息失败，则返回错误信息并终止请求处理
		ctx.JSON(http.StatusOK, ge.Response{Success: false, Data: err.Error()})
		ctx.Abort()
		return
	}
	if claims == nil {
		// 如果用户信息为空，则返回未登录错误并终止请求处理
		ctx.JSON(http.StatusOK, ge.Response{Success: false, Data: "未登录", Total: 0})
		ctx.Abort()
		return
	}

	// 获取请求路径和方法
	userName := claims.User.Name
	urlPath := ctx.Request.URL.Path
	act := ctx.Request.Method

	// 调用 Enforce 进行权限检查
	// 这里检查用户是否有权限进行指定的操作
	allowed, err := cas.GetCasbinService().Enforce(userName, appName, urlPath, act)
	if err != nil {
		// 如果权限检查失败，则返回错误信息并终止请求处理
		ctx.JSON(http.StatusOK, ge.Response{Success: false, Data: err.Error()})
		ctx.Abort()
		return
	}

	if !allowed {
		// 如果用户权限不足，则返回403错误并终止请求处理
		ctx.JSON(http.StatusOK, ge.Response{Success: false, Data: "权限不足"})
		ctx.Abort()
		return
	}

	// 如果权限检查通过，继续处理请求
	ctx.Next()
}

// addUserToRoleHandler 返回一个处理将用户添加到角色请求的函数
// @param cs Casbin 服务实例
// @return 处理添加用户到角色请求的函数
func addUserToRoleHandler(ctx *gin.Context) (errRtn error) {
	var req userRoleRequest

	// 绑定请求体并检查错误
	if err := ctx.BindJSON(&req); err != nil {
		errRtn = errors.Wrap(err, "绑定 JSON")
		return
	}

	if len(req.Users) == 0 || req.Role == "" {
		errRtn = errors.New("用户名或角色不能为空")
		return
	}
	// 判断角色是否存在
	role, err := casdoorsdk.GetRole(req.Role)
	if err != nil {
		return err
	}
	if role == nil {
		return errors.New("角色不存在")
	}
	// 判断 users 中是否有 test01
	for _, user := range req.Users {
		if !slices.Contains(role.Users, user) {
			role.Users = append(role.Users, user)
			// 同步到 casbin 数据库中
			_, err = zd.InsertMap[Policy](ctxDao, map[string]any{"PType": "g", "Role": user, "PermissionID ": req.Role})
			if err != nil {
				errRtn = errors.Wrap(err, "插入 casbin 数据库")
				return
			}
		}
	}
	_, err = casdoorsdk.UpdateRole(role)
	if err != nil {
		errRtn = errors.Wrap(err, "更新角色")
	}
	return
}

// removeUserToRoleHandler 返回一个处理从角色中移除用户请求的函数
// @param cs Casbin 服务实例
// @return 处理从角色中移除用户请求的函数
func removeUserToRoleHandler(ctx *gin.Context) (errRtn error) {
	var req userRoleRequest
	// 绑定请求体并检查错误
	if err := ctx.BindJSON(&req); err != nil {
		errRtn = errors.Wrap(err, "绑定 JSON")
		return
	}
	if len(req.Users) == 0 || req.Role == "" {
		errRtn = errors.New("用户名或角色不能为空")
		return
	}
	// 获取角色信息
	role, err := casdoorsdk.GetRole(req.Role)
	if err != nil {
		return err
	}
	if role == nil {
		return errors.New("角色不存在")
	}
	// 判断 users 中是否有 test01
	for _, user := range req.Users {
		if slices.Contains(role.Users, user) {
			role.Users = slices.DeleteFunc(role.Users, func(u string) bool { return u == user })
			// 同步到 casbin 数据库中
			_, err = zd.Delete[Policy](ctxDao, map[string]any{"PType": "g", "Role": user, "PermissionID ": req.Role})
			if err != nil {
				errRtn = errors.New("删除失败")
				return
			}
		}
	}
	_, err = casdoorsdk.UpdateRole(role)
	if err != nil {
		errRtn = errors.New("更新 role 失败")
	}
	return
}

// getUserRolesHandler 返回一个处理获取用户角色请求的函数
// @param cs Casbin 服务实例
// @return 处理获取用户角色请求的函数
func getUserRolesHandler(ctx *gin.Context) (data any, total int, errRtn error) {
	var req userRolesRequest

	// 绑定请求体并检查错误
	if err := ctx.BindJSON(&req); err != nil {
		errRtn = errors.Wrap(err, "绑定 JSON")
		return
	}

	// 获取用户的所有角色
	roles, err := cas.GetCasbinService().GetRolesForUser(req.UserName)
	if err != nil {
		errRtn = errors.Wrap(err, "获取用户角色失败")
		return
	}

	data = roles
	total = len(roles)
	return
}

// getRoleUsersHandler 返回一个处理获取角色用户请求的函数
// @param cs Casbin 服务实例
// @return 处理获取角色用户请求的函数
func getRoleUsersHandler(ctx *gin.Context) (data any, total int, errRtn error) {
	var req roleUsersRequest

	// 绑定请求体并检查错误
	if err := ctx.BindJSON(&req); err != nil {
		errRtn = errors.Wrap(err, "绑定 JSON")
		return
	}
	if req.Role == "" {
		errRtn = errors.New("角色不能为空")
		return
	}

	// 获取角色的所有用户
	users, err := cas.GetCasbinService().GetUsersForRole(req.Role)
	if err != nil {
		errRtn = errors.Wrap(err, "获取角色用户失败")
		return
	}

	data = users
	total = len(users)
	return
}

// processCasdoorUser 处理 Casdoor 用户的 CRUD 操作。
// 该函数根据传入的参数决定执行哪种操作类型（POST, POSTGet, PUT, DEL）。
// 参数:
//
//	ctx *gin.Context: Gin 框架的上下文，用于处理 HTTP 请求和响应。
//	POST, POSTGet, PUT, DEL bool: 操作类型的标志，分别表示创建、获取、更新和删除用户。
//
// 返回值:
//
//	data any: 操作返回的数据，类型不定。
//	total int: 总数，通常用于分页查询。
//	errRtn error: 错误信息，如果操作成功，则为 nil。
func processCasdoorUser(ctx *gin.Context) (data any, total int, errRtn error) {
	switch ctx.Request.Method {
	case http.MethodGet:
		data, errRtn = casdoorsdk.GetUsers()
	case http.MethodPost:
		// 处理创建用户请求
		user := casdoorsdk.User{}
		errRtn = ctx.ShouldBindJSON(&user)
		if errRtn != nil {
			errRtn = errors.Wrap(errRtn, "解析 JSON 失败")
			return
		}

		user.Id = uuid.NewString()
		user.Owner = orgName
		user.SignupApplication = appName
		user.CreatedTime = time.Now().Local().Format(time.DateTime)
		_, errRtn = casdoorsdk.AddUser(&user)
	case http.MethodPut:
		// 处理更新用户请求。
		where, set, _ := ge.PutParams(ctx)
		var id string
		if tmp, ok := where["id"]; !ok {
			errRtn = errors.New("缺少 id 字段")
			return
		} else {
			id = tmp.(string)
		}
		user, err := casdoorsdk.GetUserByUserId(id)
		if err != nil {
			errRtn = errors.Wrap(err, "查用户信息:"+id)
			return
		}

		// 根据 set 中的值更新 user
		// 将 user 转为 map[string]any 格式
		bs, _ := json.Marshal(user)
		setMap := make(map[string]any)
		json.Unmarshal(bs, &setMap)
		for k, v := range set {
			setMap[k] = v
		}
		// 将 setMap 转为 user
		bs, _ = json.Marshal(setMap)
		json.Unmarshal(bs, &user)
		_, errRtn = casdoorsdk.UpdateUserById(user.GetId(), user)
	case http.MethodDelete:
		// 处理删除用户请求。
		data, _ := ge.PostParams(ctx)
		id := data["Id"]
		_, errRtn = casdoorsdk.DeleteUser(&casdoorsdk.User{Id: id.(string)})
	}
	return
}
